/* 
 * Copyright 2009 HIT-CIR (HIT Center for Information Retrieval). 
 * 
 * author: chuter
 * mail:   lliu@ir.hit.edu.cn
 */
package edu.hit.irlab.coref.postprocess;

import java.util.ArrayList;
import java.util.HashMap;

import edu.hit.irlab.coref.mentionfinder.Mention;
import edu.hit.irlab.coref.postprocess.Postprocessor;
import edu.hit.irlab.coref.resolve.Entity;

/**
 * The Post-Processor that just merges the <i>Entities</i>
 * , which share at least one <i>Mention</i><br>
 * It suppose that the <i>Coreference Relation</i> satisfy
 * the transitive closure
 * 
 * @author chuter
 *
 */
public class DefaultPostprocessor implements Postprocessor {
	//map the id of a Mention to the id of a cluster,
	//the key is Mention's id, while the value is the cluster's id
	private static HashMap<Integer, Integer> idMap;
	
	
	/** check whether the contents of the two Mentions are
	 *  match( after filter the characters that have nothing to do
	 *  with the matching contrast, like the punctuation characters ) */
	private boolean isMentionContMatch(String menStrA, String menStrB) {
		menStrA = menStrA.replaceAll("\\pP|\\s*", "");
		menStrB = menStrB.replaceAll("\\pP\\s*", "");
		
		if (menStrA.equalsIgnoreCase(menStrB))
			return true;
		return false;
	}
	
	/** check whether two <i>Mentions</i> refer to the same 
	 *  <i>Entity</i> just by the <i>string info.</i> */
	private boolean isMentionSameEntity(Mention mentionA, Mention mentionB) {
		//just for person name and ProperName
		if (mentionA.getPersonName() && mentionB.getPersonName())
			return isMentionContMatch(mentionA.getExtent(), mentionB.getExtent());
		
		if (mentionA.getProperName() && mentionB.getProperName()) {
			if (
					mentionA.getMentionSubType() == null 
				|| 
					mentionB.getMentionSubType() == null
				)
			{ return isMentionContMatch(mentionA.getExtent(), mentionB.getExtent()); }
			else {
				if (mentionA.getMentionSubType().equals(mentionB.getMentionSubType()))
					return isMentionContMatch(mentionA.getExtent(), mentionB.getExtent());
			}
		}
		
		return false;
	}
	
	/** check whether there exits another Mention in pre-clusters 
	 *  that with the same extent with the Mentions of this cluster */
	private int getMatchClusterId(
			ArrayList<Entity> clusters, 
			ArrayList<Mention> curMentionLis
		) {
		for (int i = 0; i < clusters.size(); i++) {
			Entity preEntity = clusters.get(i);
			for (Mention preMention : preEntity.getmentions())
				for (Mention curMention : curMentionLis)
					if (isMentionSameEntity(preMention, curMention))
						return i;
		}
		return -1;
	}
	
	/** after merge two clusters, the <i>idMap</i> values should
	 *  be update, each Mention's id map to new cluster id */
	private void reMapMentions(ArrayList<Mention> mentionLis, int new_entId) {
		for (Mention mention : mentionLis)
			if (!idMap.containsKey(mention.mention_index))
				idMap.put(mention.mention_index, new_entId);
	}
	
	/** get the <i>Mention</i> clusters by just merge the
	 * 	Coreference Pairs */
	public ArrayList<Entity>  postProcess(ArrayList<Entity> entitylist) {
		idMap = new HashMap<Integer, Integer>();
		//the clusters of the Mentions
		ArrayList<Entity> clusters = new ArrayList<Entity>();
		
		int cluster_id = 0;
		for (Entity entity: entitylist) {
			ArrayList<Mention> mentionLis = entity.getmentions();
			int pre_chusterId = -1;
			for (Mention mention : mentionLis) {
				int mentionId = mention.mention_index;
				if (idMap.containsKey(mentionId)) {
					//invoke the merge action
					pre_chusterId = idMap.get(mentionId);
					Entity preEntity = clusters.get(pre_chusterId);
					//merge the two Entities
					preEntity.union(entity);
					reMapMentions(mentionLis, pre_chusterId);
					break;
				}
			}
			
			if (pre_chusterId == -1) {
				//no Mention is shared by two clusters, then check whether
				//there exits another Mention that with the same extent with
				//the Mentions of this cluster
				pre_chusterId = getMatchClusterId(clusters, mentionLis);
				if (pre_chusterId >= 0) {
					//if find, then invoke the merge action
					Entity preEntity = clusters.get(pre_chusterId);
					preEntity.union(entity);
					reMapMentions(mentionLis, pre_chusterId);
				} else {
					//if not find, then just add this single Entity to the 
					//cluster list as a cluster
					clusters.add(entity);
					reMapMentions(mentionLis, cluster_id);
					cluster_id++;
				}
			}
		}
		
		return clusters;
	}
	
}
